﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using HtmlAgilityPack;
using Telerik.WinControls;
using TrendingReader.Classes;
using CSharpVerbalExpressions;
using System.Web;
using System.Threading.Tasks;
using System.Threading;

namespace TrendingReader.V2
{
    public partial class FrmGetStoriesAsync : Telerik.WinControls.UI.RadForm
    {
        private TrendingReaderEntities _entities;
        private int _countNewItems = 0;
        private List<Chapters> _addedChapters = new List<Chapters>();
        private bool _isGetting = false;

        public FrmGetStoriesAsync()
        {
            InitializeComponent();
        }

        private void FrmGetStories_Load(object sender, EventArgs e)
        {
            _entities = new TrendingReaderEntities();
        }

        private void FrmGetStories_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (_isGetting)
            {
                RadMessageBox.Show("We are running, please wait for minutes.", "Opp!", MessageBoxButtons.OK, RadMessageIcon.Error);
                e.Cancel = true;
            }
        }

        private async void BtnGetAll_Click(object sender, EventArgs e)
        {
            if (RadMessageBox.Show("Do you want to get the stories?", "Hey!", 
                MessageBoxButtons.YesNo, RadMessageIcon.Question) == System.Windows.Forms.DialogResult.No)
            {
                return;
            }

            _isGetting = true;
            BtnGetAll.Invoke((MethodInvoker)(() => BtnGetAll.Enabled = false));
            WbpLoading.Visibility = ElementVisibility.Visible;
            WbpLoading.StartWaiting();

            await GetStoriesAsync();
            await GetChaptersAsync();

            _isGetting = false;
            BtnGetAll.Invoke((MethodInvoker)(() => BtnGetAll.Enabled = true));
            WbpLoading.Visibility = ElementVisibility.Hidden;
            WbpLoading.StopWaiting();

        }

        private async Task GetStoriesAsync()
        {
            try
            {
                await Task.Run(() =>
                {
                    var countNewStories = 0;
                    var countProcess = 0;
                    var web = new HtmlWeb();

                    CommonFunctions.AddMessageToLabel(LblMessage, "Getting resource...");
                    CommonFunctions.AddMessageToLabel(LblGetStoriesStatus, "Getting");

                    var doc = web.Load("http://truyentranhtuan.com/danh-sach-truyen");

                    if (doc == null)
                    {
                        CommonFunctions.AddMessageToLabel(LblMessage, "Cannot getting resource.");
                        CommonFunctions.AddMessageToLabel(LblGetStoriesStatus, "Error");
                        return;
                    }

                    var storiesList = doc.DocumentNode.SelectNodes("//div[@class='manga-focus']//span[@class='manga']//a");

                    if (storiesList.Count == 0)
                    {
                        return;
                    }

                    var countStoriesInDb = _entities.Stories.Count();
                    if (storiesList.Count == countStoriesInDb)
                    {
                        CommonFunctions.AddMessageToLabel(LblMessage, "We don't have any new Stories.");
                        CommonFunctions.AddMessageToLabel(LblGetStoriesStatus, "Completed");
                        CommonFunctions.AddValueToProgressBar(PgbGetStories, PgbGetStories.Maximum);
                        return;
                    }

                    // setting progress bar
                    CommonFunctions.AddValueToProgressBar(PgbGetStories, 0);
                    CommonFunctions.AddMaxValueToProgressBar(PgbGetStories, storiesList.Count);

                    foreach (var story in storiesList)
                    {
                        countProcess++;
                        CommonFunctions.AddValueToProgressBar(PgbGetStories, countProcess);

                        if (story == null || string.IsNullOrEmpty(story.Attributes["href"].Value))
                            continue;

                        var storyName = HttpUtility.HtmlDecode(story.InnerText.Trim());
                        var storyLink = story.Attributes["href"].Value.ToLower().Trim();

                        CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("Getting {0}...", storyName), 1000);

                        // check exist story
                        if (CheckExistStoryByLink(storyLink))
                        {
                            CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("\"{0}\" is existed.", storyName), 1000);
                            continue;
                        }

                        // save to database
                        var newStory = new Stories();
                        newStory.Id = Guid.NewGuid();
                        newStory.Name = storyName;
                        newStory.Link = storyLink;
                        newStory.UpdateDate = DateTime.Now;
                        newStory.RunDate = DateTime.Now;
                        _entities.Stories.Add(newStory);
                        countNewStories++;

                        CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("\"{0}\" is saved.", storyName), 1000);
                        CommonFunctions.AddMessageToLabel(LblStoriesAddedCount, countNewStories.ToString());
                    }

                    if (countNewStories > 0)
                    {
                        _entities.SaveChangesAsync();
                        CommonFunctions.AddMessageToLabel(LblMessage, string.Format("{0} new stories are added.", countNewStories));
                    }
                    else
                    {
                        CommonFunctions.AddMessageToLabel(LblMessage, "Your stories list is newest.");
                        CommonFunctions.AddValueToProgressBar(PgbGetStories, PgbGetStories.Maximum);
                    }

                    CommonFunctions.AddMessageToLabel(LblGetStoriesStatus, "Completed");
                });
            }
            catch (Exception ex)
            {
                RadMessageBox.Show(ex.Message, "Opp!", MessageBoxButtons.OK, RadMessageIcon.Error);
            }
        }

        private async Task GetChaptersAsync()
        {
            try
            {
                await Task.Run(() =>
                {
                    var countNewChapters = 0;
                    var countProcess = 0;
                    var hasChange = false;
                    var web = new HtmlWeb();
                    HtmlAgilityPack.HtmlDocument doc;

                    CommonFunctions.AddMessageToLabel(LblGetChaptersStatus, "Getting");

                    // get stories from database
                    var storiesList = _entities.Stories.OrderBy(s => s.Name).ToList();
                    if (storiesList.Count == 0)
                    {
                        CommonFunctions.AddMessageToLabel(LblMessage, "Doesnot exist any stories.");
                        return;
                    }

                    // setting progress bar
                    countProcess = 0;
                    CommonFunctions.AddValueToProgressBar(PgbGetChapters, countProcess);
                    CommonFunctions.AddMaxValueToProgressBar(PgbGetChapters, storiesList.Count);

                    // foreach story to get chapters
                    foreach (var story in storiesList)
                    {
                        // change process percent
                        countProcess++;
                        CommonFunctions.AddValueToProgressBar(PgbGetChapters, countProcess);

                        if (story == null || string.IsNullOrEmpty(story.Link))
                            continue;

                        hasChange = false;
                        CommonFunctions.AddMessageToLabel(LblMessage, string.Format("Getting {0}...", story.Name));

                        // get link
                        try
                        {
                            doc = web.Load(story.Link);
                        }
                        catch (Exception)
                        {
                            continue;
                        }

                        if (doc == null)
                        {
                            CommonFunctions.AddMessageToLabel(LblMessage, string.Format("Cannot load Resource."));
                            return;
                        }

                        var chaptersList = doc.DocumentNode.SelectNodes("//div[@id='main-content']//div[@id='manga-chapter']//span[@class='chapter-name']//a");

                        if (chaptersList == null || chaptersList.Count == 0)
                        {
                            CommonFunctions.AddMessageToLabel(LblMessage, string.Format("{0} doesnot exist any chapters.", story.Name));
                            CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("{0} doesnot exist any chapters.", story.Name), 1000);
                            continue;
                        }

                        var countChaptersInDb = _entities.Chapters.Where(c => c.StoryId == story.Id).Count();

                        if (chaptersList.Count == countChaptersInDb)
                        {
                            CommonFunctions.AddMessageToLabel(LblMessage, string.Format("We don't have any new for {0}.", story.Name));
                            CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("We don't have any new for {0}.", story.Name), 1000);
                            continue;
                        }

                        var lastChapterOrderIndex = 0;
                        var lastChapterLink = string.Empty;
                        // get last chapter that is gotten:
                        var lastChapterInDb = _entities.Chapters.Where(c => c.StoryId == story.Id).OrderByDescending(c => c.OrderIndex).FirstOrDefault();
                        if (lastChapterInDb != null)
                        {
                            lastChapterOrderIndex = lastChapterInDb.OrderIndex;
                            lastChapterLink = lastChapterInDb.Link.ToLower().Trim();
                        }
                        
                        var orderIndex = 0;
                        for (int i = chaptersList.Count - 1; i >= 0; i--)
                        {
                            orderIndex++;
                            var chapter = chaptersList[i];
                            if (string.IsNullOrEmpty(chapter.Attributes["href"].Value.ToLower().Trim()))
                            {
                                continue;
                            }

                            if (lastChapterOrderIndex != 0 && orderIndex <= lastChapterOrderIndex)
                            {
                                continue;
                            }

                            var chapterName = HttpUtility.HtmlDecode(chapter.InnerText.Trim());
                            var chapterLink = chapter.Attributes["href"].Value.ToLower().Trim();

                            CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("Getting \"{0}\"...", chapterName), 1000);

                            // check exist chapter
                            if (CheckExistChapterByLink(chapterLink))
                            {
                                CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("\"{0}\" is existed.", chapterName), 1000);
                                continue;
                            }

                            // save data to database
                            var newChapter = new Chapters();
                            newChapter.Id = Guid.NewGuid();
                            newChapter.StoryId = story.Id;
                            newChapter.Name = chapterName;
                            newChapter.UpdateDate = DateTime.Now;
                            newChapter.RunDate = DateTime.Now;
                            newChapter.Link = chapterLink;
                            newChapter.OrderIndex = orderIndex;
                            _entities.Chapters.Add(newChapter);
                            countNewChapters++;
                            hasChange = true;
                            // add chapter to temp chapters list
                            _addedChapters.Add(newChapter);

                            CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("\"{0}\" is saved.", chapterName), 1000);
                            CommonFunctions.AddMessageToLabel(LblChaptersAddedCount, countNewChapters.ToString());
                        }

                        if (hasChange)
                        {
                            try
                            {
                                story.UpdateDate = DateTime.Now;
                                _entities.SaveChangesAsync();
                            }
                            catch (Exception)
                            {
                            }
                        }
                    }

                    var message = countNewChapters > 0 ? string.Format("{0} new chapters are added.", countNewChapters) : "Your chapters list is newest.";
                    CommonFunctions.AddMessageToLabel(LblMessage, message);
                    CommonFunctions.AddMessageToLabel(LblGetChaptersStatus, "Completed");
                    if (countNewChapters == 0)
                    {
                        CommonFunctions.AddValueToProgressBar(PgbGetChapters, PgbGetChapters.Maximum);
                    }
                });
            }
            catch (Exception ex)
            {
                RadMessageBox.Show(ex.Message, "Opp!", MessageBoxButtons.OK, RadMessageIcon.Error);
                CommonFunctions.AddMessageToLabel(LblMessage, ex.Message);
                return;
            }
        }

        private void GetItemsForeachChapter(Chapters chapter)
        {
            try
            {
                var hasChange = false;
                var web = new HtmlWeb();
                HtmlAgilityPack.HtmlDocument doc;

                if (chapter == null || string.IsNullOrEmpty(chapter.Link))
                    return;

                CommonFunctions.AddMessageToLabel(LblMessage, string.Format("Getting {0}...", chapter.Name));

                // get link
                doc = web.Load(chapter.Link);

                if (doc == null)
                {
                    CommonFunctions.AddMessageToLabel(LblMessage, "Cannot load Resource.");
                    return;
                }

                var htmlText = doc.DocumentNode.InnerHtml;

                var isGoogleLink = true;
                var strLinks = string.Empty;
                string[] linksList = { };

                var verbEx = new VerbalExpressions()
                    .Then("var slides_page_url_path = [")
                    .Anything()
                    .Then("];");

                strLinks = verbEx.Capture(htmlText, "2");

                if (string.IsNullOrEmpty(strLinks))
                {
                    verbEx = new VerbalExpressions()
                    .Then("var slides_page_path = [")
                    .Anything()
                    .Then("];");
                    isGoogleLink = false;
                    strLinks = verbEx.Capture(htmlText, "2");
                }

                if (string.IsNullOrEmpty(strLinks))
                {
                    CommonFunctions.AddMessageToLabel(LblMessage, string.Format("{0} doesnot exist content.", chapter.Name));
                    CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("{0} doesnot exist content.", chapter.Name), 1000);
                    return;
                }

                if (strLinks.Contains(","))
                {
                    linksList = strLinks.Split(',');
                }
                else if (strLinks.Contains("|"))
                {
                    linksList = strLinks.Split('|');
                }

                var itemsList = new List<string>();
                foreach (var link in linksList)
                {
                    itemsList.Add(link.Trim().Replace("\"", ""));
                }

                var countItemsInDb = _entities.Items.Where(i => i.ChapterId == chapter.Id).Count();

                if (itemsList.Count == 0 || itemsList.Count == countItemsInDb)
                {
                    CommonFunctions.AddMessageToLabel(LblMessage, string.Format("We don't have new content for {0}.", chapter.Name));
                    CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("We don't have new content for {0}.", chapter.Name), 1000);
                    return;
                }

                var orderIndex = 0;

                if (!isGoogleLink)
                    itemsList.OrderBy(i => i);

                foreach (var item in itemsList)
                {
                    orderIndex++;

                    if (string.IsNullOrEmpty(item))
                        continue;

                    var imgName = CommonFunctions.GenerateFileName(chapter.Name, orderIndex.ToString());
                    if (CheckExistChapterItemByLink(item))
                    {
                        CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("\"{0}\" is existed.", imgName), 1000);
                        continue;
                    }

                    // save data to database
                    var newItem = new Items();
                    newItem.Id = Guid.NewGuid();
                    newItem.ChapterId = chapter.Id;
                    newItem.Name = imgName;
                    newItem.Link = item;
                    newItem.UpdateDate = DateTime.Now;
                    newItem.RunDate = DateTime.Now;
                    newItem.OrderIndex = orderIndex;
                    _entities.Items.Add(newItem);
                    _countNewItems++;
                    hasChange = true;

                    CommonFunctions.AddMessageToListBox(LsbMessage, string.Format("\"{0}\" is saved.", imgName), 1000);
                }

                if (hasChange)
                {
                    try
                    {
                        chapter.UpdateDate = DateTime.Now;
                        chapter.Stories.UpdateDate = DateTime.Now;
                        _entities.SaveChangesAsync();
                    }
                    catch (Exception)
                    {
                        return;
                    }
                }
            }
            catch (Exception)
            {
                return;
            }
        }

        private bool CheckExistStoryByLink(string link)
        {
            var blnExisted = false;

            var story = _entities.Stories.FirstOrDefault(s => s.Link.ToLower().Trim() == link.ToLower().Trim());

            blnExisted = (story != null);

            return blnExisted;
        }

        private bool CheckExistChapterByLink(string link)
        {
            var blnExisted = false;

            var story = _entities.Chapters.FirstOrDefault(s => s.Link.ToLower().Trim() == link.ToLower().Trim());

            blnExisted = (story != null);

            return blnExisted;
        }

        private bool CheckExistChapterItemByLink(string link)
        {
            var blnExisted = false;

            var item = _entities.Items.FirstOrDefault(s => s.Link.ToLower().Trim() == link.ToLower().Trim());

            blnExisted = (item != null);

            return blnExisted;
        }

    }
}
